// Created on: 1992-07-02
// Created by: Philippe DAUTRY
// Copyright (c) 1992-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.


#include <BRepSweep_Builder.hxx>
#include <BRepSweep_Iterator.hxx>
#include <BRepSweep_NumLinearRegularSweep.hxx>
#include <BRepSweep_Tool.hxx>
#include <Standard_DomainError.hxx>
#include <Standard_NoMoreObject.hxx>
#include <Standard_NoSuchObject.hxx>
#include <Standard_RangeError.hxx>
#include <Sweep_NumShape.hxx>
#include <Sweep_NumShapeIterator.hxx>
#include <Sweep_NumShapeTool.hxx>
#include <TopAbs.hxx>
#include <TopAbs_Orientation.hxx>
#include <TopoDS_Shape.hxx>
#include <TopTools_SequenceOfShape.hxx>
#include <TopoDS.hxx>

//=======================================================================
//function : BRepSweep_NumLinearRegularSweep
//purpose  : Create a Regular Sweep.
//=======================================================================
BRepSweep_NumLinearRegularSweep::BRepSweep_NumLinearRegularSweep
  (const BRepSweep_Builder& aBuilder,
  const TopoDS_Shape& aGenShape,
  const Sweep_NumShape& aDirShape):

myBuilder(aBuilder),
  myGenShape(aGenShape),
  myDirWire(aDirShape),

  myGenShapeTool(aGenShape),
  myDirShapeTool(aDirShape),

  // *****************************************************************
  // Les Tableaux
  // *****************************************************************

  myShapes(1,myGenShapeTool.NbShapes(),
  1,myDirShapeTool.NbShapes()),
  myBuiltShapes(1,myGenShapeTool.NbShapes(),
  1,myDirShapeTool.NbShapes()),
  myUsedShapes(1, myGenShapeTool.NbShapes(),
  1, myDirShapeTool.NbShapes())
{
  myBuiltShapes.Init(Standard_False); 
  myUsedShapes.Init(Standard_False);
}

//=======================================================================
//function : ~BRepSweep_NumLinearRegularSweep
//purpose  : Destructor
//=======================================================================

BRepSweep_NumLinearRegularSweep::~BRepSweep_NumLinearRegularSweep()
{
}

//=======================================================================
//function : Shape
//purpose  : Returns the global Shape.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::Shape () 
{
  if (HasShape(myGenShape,myDirWire)) return Shape(myGenShape,myDirWire);
  else {
    TopoDS_Shape bidon;
    return bidon;
  }
}


//=======================================================================
//function : Shape
//purpose  : Returns the Shape generated with aGenS.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::Shape (const TopoDS_Shape& aGenS) 
{
  if (myGenShapeTool.Index(aGenS) != 0 &&
    HasShape(aGenS,myDirWire)) return Shape(aGenS,myDirWire);
  else {
    TopoDS_Shape bidon;
    return bidon;
  }
}


//=======================================================================
//function : Shape
//purpose  : Returns the Shape indexed by the arguments.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::Shape (const TopoDS_Shape& aGenS, 
  const Sweep_NumShape& aDirS)
{
  Standard_Integer iGenS = myGenShapeTool.Index(aGenS);
  Standard_Integer iDirS = myDirShapeTool.Index(aDirS);
  if (!myBuiltShapes(iGenS,iDirS)){
    TopoDS_Shape newShape;
    TopoDS_Shape bGenS,cGenS,subGenS,subsubGenS;
    Sweep_NumShape bDirS,subDirS;
    BRepSweep_Iterator It;
    Sweep_NumShapeIterator Kt;
    BRepSweep_Iterator Lt;
    TopAbs_Orientation Or,Pr;
    if (myDirShapeTool.Type(aDirS)==TopAbs_VERTEX){
      //Ici on construit les "planchers" du Shape.
      TopAbs_ShapeEnum aGenSType = myGenShapeTool.Type(aGenS);
      switch (aGenSType){
      case TopAbs_VERTEX : 
        myShapes(iGenS,iDirS)=MakeEmptyVertex(aGenS,aDirS);
        break;
      case TopAbs_EDGE :
        myShapes(iGenS,iDirS)=MakeEmptyGeneratingEdge(aGenS,aDirS);
        break;
      case TopAbs_WIRE :
        myBuilder.MakeWire(myShapes(iGenS,iDirS));
        break;
      case TopAbs_FACE :
        myShapes(iGenS,iDirS)=MakeEmptyFace(aGenS,aDirS);
        break;
      case TopAbs_SHELL :
        myBuilder.MakeShell(myShapes(iGenS,iDirS));
        break;
      case TopAbs_SOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPSOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPOUND :
        myBuilder.MakeCompound(myShapes(iGenS,iDirS));
        break;
      default:
        throw Standard_NoSuchObject("Unknown Shape");
        break;
      }
      bGenS = aGenS;
      myGenShapeTool.SetOrientation(bGenS,TopAbs_FORWARD);
      for (It.Init(bGenS);It.More();It.Next()){
        subGenS = It.Value();
        Or = It.Orientation();
        if(HasShape(subGenS,aDirS)){
          newShape = Shape(subGenS,aDirS);
          Standard_Integer iNewGenS = myGenShapeTool.Index(subGenS);
          Standard_Integer iNewDirS = iDirS;
          if (GGDShapeIsToAdd(myShapes(iGenS, iDirS), newShape,
            aGenS,subGenS,aDirS)){
              //Les "planchers" doivent etre construits par les 
              //fonctions de construcion geometrique identiquement 
              //au shape generateur.
              //On leur recolle juste une orientation pour etre bien 
              //sur.

              myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
              myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              TopAbs_ShapeEnum subGenSType = myGenShapeTool.Type(subGenS);
              if (aGenSType==TopAbs_FACE){   
                if(subGenSType==TopAbs_VERTEX){
                  SetParameters(myShapes(iGenS,iDirS),
                    newShape,aGenS,subGenS,aDirS);
                }
                else if(subGenSType==TopAbs_EDGE){
                  SetPCurve(myShapes(iGenS,iDirS),newShape,
                    aGenS,subGenS,aDirS,Or);
                }	    
                else if(subGenSType==TopAbs_WIRE){
                  BRepSweep_Iterator Jt;
                  cGenS = subGenS;
                  myGenShapeTool.SetOrientation(cGenS,TopAbs_FORWARD);
                  for (Jt.Init(cGenS);Jt.More();Jt.Next()){
                    subsubGenS = Jt.Value();
                    Pr = Jt.Orientation();
                    if(HasShape(subsubGenS,aDirS)){
                      TopoDS_Shape newsubEdge = Shape(subsubGenS,aDirS);
                      SetPCurve(myShapes(iGenS,iDirS),newsubEdge,
                        aGenS,subsubGenS,aDirS,Pr);
                    }
                  }
                }
              }
              else if(aGenSType==TopAbs_EDGE){
                SetGeneratingParameter(myShapes(iGenS,iDirS),
                  newShape,bGenS,subGenS,aDirS);
              }
          }
        }
      }
    }
    else if (myDirShapeTool.Type(aDirS)==TopAbs_EDGE){
      //Ici on construit les murs du Shape.
      TopAbs_ShapeEnum aGenSType = myGenShapeTool.Type(aGenS);
      TopoDS_Shape newWire,newShell;
      TopTools_SequenceOfShape WireSeq;
      Standard_Boolean sepwires = Standard_False;
      switch (aGenSType){
      case TopAbs_VERTEX : 
        myShapes(iGenS,iDirS)=MakeEmptyDirectingEdge(aGenS,aDirS);
        break;
      case TopAbs_EDGE :
        //On cree un wire intermediaire qui contient tous les edges
        //du montant (face) du Shape pour le cas standard, et une 
        //sequence de wires pour les cas merdiques necessitant des
        //wires independants.
        myBuilder.MakeWire(newWire);
        myShapes(iGenS,iDirS)=MakeEmptyFace(aGenS,aDirS);
        break;
      case TopAbs_WIRE :
        myBuilder.MakeShell(myShapes(iGenS,iDirS));
        break;
      case TopAbs_FACE :
        //On cree un shell intermediaire dans lequel on jette toutes 
        //les faces en direct, pour eviter les empilages compliques 
        //de shells et sous shells dans la structure du solide.
        myBuilder.MakeShell(newShell);
        myBuilder.MakeSolid(myShapes(iGenS,iDirS));
        break;
      case TopAbs_SHELL :
        myBuilder.MakeCompSolid(myShapes(iGenS,iDirS));
        break;
      case TopAbs_SOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPSOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPOUND :
        myBuilder.MakeCompound(myShapes(iGenS,iDirS));
        break;
      default:
        throw Standard_NoSuchObject("Unknown Shape");
        break;
      }
      bGenS = aGenS;
      myGenShapeTool.SetOrientation(bGenS,TopAbs_FORWARD);
      for (It.Init(bGenS);It.More();It.Next()){
        subGenS = It.Value();
        if(HasShape(subGenS,aDirS)){
          newShape = Shape(subGenS,aDirS);
          Standard_Integer iNewGenS = myGenShapeTool.Index(subGenS);
          Standard_Integer iNewDirS = iDirS;
          if (GGDShapeIsToAdd(myShapes(iGenS, iDirS), newShape,
            aGenS,subGenS,aDirS)){
              TopAbs_ShapeEnum subGenSType = myGenShapeTool.Type(subGenS);
              if (aGenSType==TopAbs_EDGE){   
                Or = It.Orientation();
                if (SeparatedWires(myShapes(iGenS,iDirS),newShape,
                  aGenS,subGenS,aDirS)){
                    sepwires = Standard_True;
                    TopoDS_Shape wi;
                    myBuilder.MakeWire(wi);
                    myBuilder.Add(wi,newShape,Or);
                    myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
                    wi.Closed(BRep_Tool::IsClosed(wi));
                    WireSeq.Append(wi);
                }
                else{
                  myBuilder.Add(newWire,newShape,Or);
                  myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
                }
                SetDirectingPCurve (myShapes(iGenS,iDirS),
                  newShape,bGenS,subGenS,aDirS,Or);
              }
              else if (aGenSType==TopAbs_WIRE){
                Or = It.Orientation();
                myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              }
              else if (aGenSType==TopAbs_FACE){
                Or = It.Orientation();
                if(subGenSType == TopAbs_WIRE) {
                  for (Lt.Init(newShape);Lt.More();Lt.Next()){
                    myBuilder.Add(newShell,Lt.Value(),
                      TopAbs::Compose(Lt.Orientation(),Or));
                  }
                }
                else if(subGenSType == TopAbs_EDGE) {
                  myBuilder.Add(newShell,newShape,Or);
                  myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
                }
              }
              else if(aGenSType == TopAbs_SHELL){
                Or = TopAbs_FORWARD;
                myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              }
              else if(aGenSType == TopAbs_COMPOUND){
                Or = TopAbs_FORWARD;
                myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              }
              else{
                Or = It.Orientation();
                myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              }
          }
        }
      }
      bDirS = aDirS;
      for (Kt.Init(bDirS);Kt.More();Kt.Next()){
        subDirS = Kt.Value();
        if(HasShape(aGenS,subDirS)){
          newShape = Shape(aGenS,subDirS);
          Standard_Integer iNewGenS = iGenS;
          Standard_Integer iNewDirS = myDirShapeTool.Index(subDirS);
          if (GDDShapeIsToAdd(myShapes(iGenS,iDirS),newShape,
            aGenS,aDirS,subDirS)){
              if (aGenSType==TopAbs_EDGE){   
                Or = TopAbs::Reverse(Kt.Orientation());
                myBuilder.Add(newWire,newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
                SetGeneratingPCurve
                  (myShapes(iGenS,iDirS),newShape,aGenS,aDirS,subDirS,Or);
              }
              else if(aGenSType==TopAbs_VERTEX){
                Or = Kt.Orientation();
                myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
                SetDirectingParameter
                  (myShapes(iGenS,iDirS),newShape,aGenS,aDirS,subDirS);
              }
              else if(aGenSType==TopAbs_FACE){
                Or = Kt.Orientation();
                myBuilder.Add(newShell,newShape,Or);
                myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
              }
          }
        }
      }
      if (aGenSType==TopAbs_EDGE){
        if (sepwires){
          for(Standard_Integer ij = 1;ij <= WireSeq.Length();ij++){
            myBuilder.Add(myShapes(iGenS,iDirS),WireSeq.Value(ij));
          }
        }
        else{
          newWire.Closed(BRep_Tool::IsClosed(newWire));
          myBuilder.Add(myShapes(iGenS,iDirS),newWire);
        }
        myBuiltShapes(iGenS,iDirS) = Standard_True;
        SetContinuity(aGenS,aDirS);
      }
      if (aGenSType==TopAbs_WIRE){
        SetContinuity(aGenS,aDirS);
      }	
      if (aGenSType==TopAbs_FACE){
        newShell.Closed (BRep_Tool::IsClosed (newShell));
        TopoDS_Shape temp = SplitShell(newShell);
        TopAbs_Orientation ShellOri = DirectSolid(aGenS,aDirS);
        Lt.Init(temp);
        if(Lt.More()) Lt.Next();
        if(Lt.More()){
          for (Lt.Init(temp);Lt.More();Lt.Next()){
            myBuilder.Add(myShapes(iGenS,iDirS),Lt.Value(), ShellOri);
          }
        }
        else myBuilder.Add(myShapes(iGenS,iDirS),newShell, ShellOri);
      }
    }
    else if (myDirShapeTool.Type(aDirS)==TopAbs_WIRE){
      TopAbs_ShapeEnum aGenSType = myGenShapeTool.Type(aGenS);
      switch (aGenSType){
      case TopAbs_VERTEX : 
        myBuilder.MakeWire(myShapes(iGenS,iDirS));
        break;
      case TopAbs_EDGE :
        myBuilder.MakeShell(myShapes(iGenS,iDirS));
        break;
      case TopAbs_WIRE :
        myBuilder.MakeShell(myShapes(iGenS,iDirS));
        break;
      case TopAbs_FACE :
        myBuilder.MakeCompSolid(myShapes(iGenS,iDirS));
        break;
      case TopAbs_SHELL :
        myBuilder.MakeCompSolid(myShapes(iGenS,iDirS));
        break;
      case TopAbs_SOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPSOLID :
        throw Standard_NoSuchObject("Solids are not Processed");
        break;
      case TopAbs_COMPOUND :
        myBuilder.MakeCompound(myShapes(iGenS,iDirS));
        break;
      default:
        throw Standard_NoSuchObject("Unknown Shape");
        break;
      }
      bDirS = aDirS;
      for (Kt.Init(aDirS);Kt.More();Kt.Next()){
        subDirS = Kt.Value();
        if(HasShape(aGenS,subDirS)){
          Standard_Integer iNewGenS = iGenS;
          Standard_Integer iNewDirS = myDirShapeTool.Index(subDirS);
          Or = Kt.Orientation();
          newShape = Shape(aGenS,subDirS);
          myBuilder.Add(myShapes(iGenS,iDirS),newShape,Or);
          myUsedShapes(iNewGenS, iNewDirS) = Standard_True;
        }
      }
    }
    myBuiltShapes(iGenS,iDirS) = Standard_True;
  }
  // Change the "Closed" flag only for Wires and Shells
  if (myShapes(iGenS, iDirS).ShapeType() == TopAbs_WIRE ||
    myShapes(iGenS, iDirS).ShapeType() == TopAbs_SHELL)
    myShapes(iGenS,iDirS).Closed (BRep_Tool::IsClosed (myShapes(iGenS,iDirS)));
  return myShapes(iGenS,iDirS);
}


//=======================================================================
//function : FirstShape
//purpose  : Returns the Shape indexed by the arguments.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::FirstShape ()
{
  TopoDS_Shape result;
  if (myDirShapeTool.HasFirstVertex()){
    if(HasShape(myGenShape,myDirShapeTool.FirstVertex()))
      result = Shape(myGenShape,myDirShapeTool.FirstVertex());
  }
  return result;
}


//=======================================================================
//function : LastShape
//purpose  : Returns the Shape indexed by the arguments.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::LastShape ()
{
  TopoDS_Shape result;
  if (myDirShapeTool.HasLastVertex()){
    if(HasShape(myGenShape,myDirShapeTool.LastVertex()))
      result = Shape(myGenShape,myDirShapeTool.LastVertex());
  }
  return result;
}


//=======================================================================
//function : FirstShape
//purpose  : Returns the Shape indexed by the arguments.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::FirstShape (const TopoDS_Shape& aGenS)
{
  TopoDS_Shape result;
  if (myDirShapeTool.HasFirstVertex()){
    if(HasShape(aGenS,myDirShapeTool.FirstVertex()))
      result = Shape(aGenS,myDirShapeTool.FirstVertex());
  }
  return result;
}


//=======================================================================
//function : LastShape
//purpose  : Returns the Shape indexed by the arguments.
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::LastShape (const TopoDS_Shape& aGenS)
{
  TopoDS_Shape result;
  if (myDirShapeTool.HasLastVertex()){
    if(HasShape(aGenS,myDirShapeTool.LastVertex()))
      result = Shape(aGenS,myDirShapeTool.LastVertex());
  }
  return result;
}

//=======================================================================
//function : Closed
//purpose  : 
//=======================================================================

Standard_Boolean BRepSweep_NumLinearRegularSweep::Closed()const 
{
  return myDirWire.Closed();
}

//=======================================================================
//function : SplitShell
//purpose  : 
//=======================================================================

TopoDS_Shape BRepSweep_NumLinearRegularSweep::SplitShell(const TopoDS_Shape& aNewShape)const 
{
  TopoDS_Shape comp;
  myBuilder.MakeCompound(comp);
  myBuilder.Add(comp,aNewShape);
  return comp;
}

//=======================================================================
//function : IsUsed
//purpose  : 
//=======================================================================
Standard_Boolean BRepSweep_NumLinearRegularSweep::IsUsed(const TopoDS_Shape& aGenS) const
{
  Standard_Integer iGenS = myGenShapeTool.Index(aGenS);
  Standard_OutOfRange_Raise_if(iGenS == 0,
    "BRepSweep_NumLinearRegularSweep::IsUsed: shape index = 0")
  Standard_Integer j;
  Standard_Boolean isBuilt = Standard_False;
  Standard_Boolean isUsed = Standard_False;
  for (j = 2; j <= myBuiltShapes.UpperCol(); ++j)
  {
    isBuilt = isBuilt || myBuiltShapes(iGenS, j);
    isUsed = isUsed || myUsedShapes(iGenS, j);
  }
  if (isUsed)
  {
    if (aGenS.ShapeType() == TopAbs_VERTEX && IsInvariant(aGenS))
    {
      if (myUsedShapes(iGenS, 1) || !Closed())
      {
        return isUsed;
      }
      else
      {
        return Standard_False;
      }
    }
    else
    {
      return isUsed;
    }
  }
  //
  if (isBuilt) //&& !IsUsed
  {
    if (!HasShape(aGenS, myDirWire) && !Closed())
    {
      return Standard_True;
    }
    else if (aGenS.ShapeType() == TopAbs_VERTEX && !Closed())
    {
      if (!myBuiltShapes(iGenS, 1))
      {
        return Standard_True;
      }
    }
  }
  return isUsed;
}

//=======================================================================
//function : GenIsUsed
//purpose  : 
//=======================================================================
Standard_Boolean BRepSweep_NumLinearRegularSweep::GenIsUsed(const TopoDS_Shape& aGenS) const
{
  Standard_Integer iGenS = myGenShapeTool.Index(aGenS);
  Standard_OutOfRange_Raise_if(iGenS == 0,
    "BRepSweep_NumLinearRegularSweep::GenIsUsed: shape index = 0")
  return myBuiltShapes(iGenS, 1) && myUsedShapes(iGenS, 1);
}
